package satir.record;

import org.json.JSONException;
import org.json.JSONObject;
import satir.framework.JSONIOPerformer;
import satir.framework.JSONIOPerformer.JSONIOPerformerJSONDecodeException;
import satir.framework.JSONIOPerformer.JSONIOPerformerReadException;
import satir.framework.JSONIOPerformer.JSONIOPerformerWriteException;

/**
 * The Class SavedEntity.
 * @version 3.0 April 2012
 * @author Creative Family
 */
public abstract class SavedEntity {

    /** Input and output of profile. */
    private JSONIOPerformer io;

    /** Database filename. */
    private String dBFilename;

    /** Database directory. */
    private String dBDirectory;

    /**
     * Restore a profile saved early by given specific JSON file location.
     * @param dBfn
     *            the dB filename
     * @param dBDir
     *            the dB directory
     * @throws JSONIOPerformerReadException
     *             the jSONIO performer read exception
     * @throws JSONIOPerformerJSONDecodeException
     *             the jSONIO performer json decode exception
     * @throws JSONFileInvalidException
     *             the jSON file invalid exception
     */
    public SavedEntity(final String dBfn, final String dBDir)
            throws JSONIOPerformerReadException,
            JSONIOPerformerJSONDecodeException, JSONFileInvalidException {

	// Initialize essential objects
	this();
	this.dBDirectory = dBDir;
	this.dBFilename = dBfn;

	boolean createNew = false;

	// Read the current saved file
	try {
	    io = new JSONIOPerformer(dBDirectory + dBFilename);

	    // Initialize from JSON object
	    try {
		initializeFromJSON(io.retrieve());
	    } catch (JSONException e) {
		throw new JSONFileInvalidException(getAppellation(), dBFilename);
	    }
	} catch (JSONIOPerformerJSONDecodeException e2) {
	    createNew = true;
	} catch (JSONIOPerformerReadException e1) {
	    createNew = true;
	}

	if (createNew) {
	    try {
		io = new JSONIOPerformer(dBDirectory + dBFilename, true);
	    } catch (JSONIOPerformerReadException e) {
		e.printStackTrace();
	    }
	    createNewToJSON();
	}
    }

    /**
     * Construct an new instantiation without any restoring process.
     */
    public SavedEntity() {
	// Initialize essential objects
	initializeEssentialObjects();
    }

    /**
     * Get an instantiation of JSONIOPerformer which stored the data of
     * restoring file.
     * @return JSONIOPerformer
     */
    protected JSONObject getJSONObject() {
	return io.retrieve();
    }

    /**
     * Save to json.
     */
    public void saveToJSON() {
	try {
	    io.write();
	} catch (JSONIOPerformerWriteException e) {
	    // TODO Auto-generated catch block
	    e.printStackTrace();
	}
    }

    /**
     * To implement the structure creation for JSON, when the file is not
     * exists.
     */
    protected abstract void createNewToJSON();

    /**
     * To provide the appellation (name) of the implementation of SavedEntity.
     * @return String
     */
    protected abstract String getAppellation();

    /**
     * To implement the initialization of the essential objects This method will
     * be called when construct an instantiation.
     */
    protected abstract void initializeEssentialObjects();

    /**
     * To implement the initialization of the saved entity retrieving from
     * specific JSON data.
     * @param json
     *            the JSON
     * @throws JSONException
     *             the jSON exception
     * @throws JSONIOPerformerReadException
     *             the jSONIO performer read exception
     * @throws JSONIOPerformerJSONDecodeException
     *             the jSONIO performer JSON decode exception
     */
    protected abstract void initializeFromJSON(JSONObject json)
	    throws JSONException, JSONIOPerformerReadException,
	    JSONIOPerformerJSONDecodeException;

    /**
     * Gets the dB filename.
     * @return the dB filename
     */
    public String getDBFilename() {
	return dBFilename;
    }

    /**
     * Gets the dB directory.
     * @return the dB directory
     */
    public String getDBDirectory() {
	return dBDirectory;
    }

    /**
     * The Class JSONFileInvalidException.
     */
    public class JSONFileInvalidException extends JSONException {

	/** Incorrect profile format. */
	private static final long serialVersionUID = 5508282370402323097L;

	/**
	 * Instantiates a new jSON file invalid exception.
	 * @param filename
	 *            the filename
	 */
	public JSONFileInvalidException(final String filename) {
	    this("SavedEntity", filename);
	}

	/**
	 * Instantiates a new jSON file invalid exception.
	 * @param appellation
	 *            the appellation
	 * @param filename
	 *            the filename
	 */
	public JSONFileInvalidException(final String appellation, final String filename) {
	    super("ERROR: The JSON file is not valid to the structure of '"
		    + appellation + "'. reference: filename[" + filename + "]");
	}

    }

    /**
     * The Class InitializeFromJSONFailure.
     */
    public class InitializeFromJSONFailure extends Exception {

	/** Initialize from JSON failure. */
	private static final long serialVersionUID = 4443323744021614112L;

	/**
	 * Instantiates a new initialize from JSON failure.
	 * @param filename
	 *            the filename
	 */
	public InitializeFromJSONFailure(final String filename) {
	    this("SavedEntity", filename);
	}

	/**
	 * Instantiates a new initialize from JSON failure.
	 * @param appellation
	 *            the appellation
	 * @param filename
	 *            the filename
	 */
	public InitializeFromJSONFailure(final String appellation, final String filename) {
	    super("ERROR: The JSON file is not valid to the structure of '"
		    + appellation + "'. reference: filename[" + filename + "]");
	}

    }
}
